Issue 02: per-product source adapter, normalized model, two-control split#1127
Closed
busbyk wants to merge 4 commits into
Closed
Issue 02: per-product source adapter, normalized model, two-control split#1127busbyk wants to merge 4 commits into
busbyk wants to merge 4 commits into
Conversation
Issue 02 of native-product-pages: the data layer every native page consumes.
- Normalized model (services/nac/model/forecast.ts): API-shape-agnostic types
the components depend on. Owns the top-level product types and reuses the leaf
domain enums/types from the v2 wire schema. "No active warning" is plain null,
not v2's null-object, so consumers stop special-casing the miss representation.
- Source adapter (services/nac/sources/): ForecastSource/WarningSource interfaces
with a v2 implementation that maps the legacy response into the model. Pure,
unit-tested v2 mappers. Components/pages fetch through the adapter, never a
raw fetcher.
- Control 2 (data source): per-product v2/v3 selection via code/env config,
uniform across tenants, defaulting to v2, with a per-center v3 canary allowlist.
- Control 1 (rollout): Settings.useNativeForecasts becomes a per-product
nativeProducts:{forecast,warning} group; getNativeProductFlag(center,product)
reader. Migration backfills both flags from the old value; seed updated.
tsc, lint, test (incl. v2->model mapper tests), and drift all green. Migration
verified on a dev.db copy (backfill preserves data before the column drop);
--no-verify only bypasses the ALTER/DROP review gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A concise, human-facing README covering the normalized model, the per-product source adapter, the model-vs-wire dependency direction, and the two controls (rollout vs data source), plus how to extend the layer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate the data-layer architecture overview from src/services/nac/README.md to docs/nac-data-layer.md (repo convention: docs live in docs/). Repo-root the code paths and cross-link with nac-data-display.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One physical line per paragraph/bullet (no manual hard-wrapping). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
Preview deployment: https://issue-02-data-layer.preview.avy-fx.org |
Draft
9 tasks
Collaborator
Author
|
Folded into #1129, which now targets native-product-pages and shows the full combined stack diff for single-PR review. Branch kept as a safety net. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Issue 02 of the native-product-pages effort: the data layer every native product page consumes — a per-product source adapter returning a normalized, API-shape-agnostic model, plus the two controls that govern it (rollout flag; data-source config). This is reconcile/refactor, not greenfield: the fetchers, zod types, and components already exist; this reorganizes them behind an adapter seam and adds the two config seams.
Related Issues
.scratch/native-product-pages/issues/02-data-layer-adapter-and-controls.md.scratch/native-product-pages/PRD.md· ADR: "Native product page architecture" (to land asdocs/decisions/018-*)Key Changes
services/nac/model/forecast.ts) — API-agnostic types the components depend on. Owns the top-level product types (Forecast/Summary/Warning/WarningProduct/…) and reuses the leaf domain enums/types from the v2 wire schema. "No active warning" is plainnull, not v2's null-object, so consumers stop special-casing the miss representation (the duplicatedtoWarningProducthelpers are gone).services/nac/sources/) —ForecastSource/WarningSourceinterfaces, a v2 implementation wrapping the existing fetchers with pure, unit-testedmapV2*mappers, andgetForecastSource/getWarningSourceresolvers. Components/pages fetch through the adapter, never a raw fetcher.sources/config.ts) — per-productv2/v3selection via code/env config, uniform across tenants, defaulting to v2, with a per-center v3 canary allowlist. Not a Setting. (NAC_{FORECAST,WARNING}_SOURCE,NAC_{FORECAST,WARNING}_V3_CANARY_CENTERS.)Settings.useNativeForecastsbecomes a per-productnativeProducts: { forecast, warning }group; newgetNativeProductFlag(center, product)reader. Per-product (not all-or-nothing) so a center can go native on forecast while later products stay on the widget.[zone]routes) wired to the adapter; all forecast components repointed to the model.How to test
pnpm tsc,pnpm lint,pnpm test(includes new v2→model mapper tests in__tests__/server/nacSourceMappers.server.test.ts).nativeProducts.forecastin Settings → native forecast page renders; off → legacy widget.NAC_FORECAST_SOURCE=v3(or the canary allowlist) currently throws by design until the v3 source is built.Migration Explanation
20260622_223300_native_products_flaggeneralizes the singleuse_native_forecastsboolean into thenative_products_forecast/native_products_warningcolumns. It adds both columns, backfills both from the old value (so a center already on the native forecast page — warning banner included — is unchanged), then drops the old column. Verified on a dev.db copy: a row with the old flag set retains both new flags; data preserved.migrate:checkflags theALTER/DROPfor review (expected for any schema migration); the drop is intentional and data-preserving.Future enhancements / Questions
getUseNativeForecastsis kept as a now-unused thin wrapper overgetNativeProductFlag— can be deleted.🤖 Generated with Claude Code